© Algoritma 2024 ___
Di dalam lanskap operasional bisnis yang terus berkembang dengan cepat, menguasai pemrosesan faktur yang efektif menjadi sangat penting. Pemrosesan faktur adalah bagian kritis dari kegiatan bisnis yang melibatkan pengelolaan keuangan dan akuntansi. Faktur yang diproses dengan baik membantu dalam pelacakan pengeluaran, mengoptimalkan arus kas, dan memastikan kepatuhan terhadap peraturan keuangan. Dengan lanskap bisnis yang cepat berubah, perusahaan perlu dapat beradaptasi dengan cepat. Menguasai pemrosesan faktur menjadi penting untuk mengatasi ketidakpastian dan menjaga kecepatan tanggapan terhadap perubahan di sekitarnya.
Materi ini menawarkan solusi komprehensif dengan workshop khusus yang dilengkapi dengan keterampilan terkini. Fokusnya adalah pada penggunaan Document Understanding Transformer, sebuah model deep learning NLP, dengan pemrograman Python. Dengan menyelami teknologi ini, yang terbukti berhasil dalam tugas pemrosesan bahasa alami, peserta akan memperoleh keahlian untuk menyederhanakan dan meningkatkan alur kerja pemrosesan faktur.
Setelah menyelesaikan workshop ini, Anda akan dapat:
Python Programming Basics
Working with DataFrame Pandas
pandasDocument Understanding Transformer (Donut) Utilization
Integration Gradio for Efficient Scanning
1️⃣ Membuka Terminal pada Visual Studio Code
Saat pertama kali kita membuka Visual Studio Code, terdapat beberapa menu di pojok kiri atas. Silahkan kamu klik menu Terminal pada bagian kiri atas. Lalu, pilih New Terminal. Selesai, kamu berhasil membuka terminal baru.
2️⃣ Buat Environment dengan Nama ENV_NAME:
conda create -n dss_invoice python=3.10
Tujuan dari kita menuliskan python=3.10 agar python yang terinstall pada virtual environtment yang kita buat adalah python dengan versi 3.10.
3️⃣ Mengaktifkan Environment
Setelah virtual environtment yang baru kita buat sudah selesai, kita bisa mengaktifkan environtment yang sudah kita buat dengan code berikut.
conda activate dss_invoice
Sebelum melakukan instalasi package, pastikan Anda sudah melakukan aktivasi virtual environment tempat Anda mau menginstall package.
pip install <PACKAGE_NAME>
Kita juga bisa menginstallnya melalui sebuah teks reqirements.txt yang berisi list packages yang perlu diinstall, commandnya pip install -r requirements.txt dengan syarat sebelum menjalankan command ini, kita sudah berapa pada folder material utama
Pilihlah jawaban yang tepat dengan memberikan tanda centang pada kotak.
Berikut adalah tujuan kita membuat virtual environment, KECUALI ...
Pasangkan beberapa opsi ini dengan pilihan yang cocok:
Tipe cell dalam notebook:
1️⃣ Markdown : untuk menuliskan narasi
Ini adalah cell markdown. Kita bisa menulis teks bold, italic, bahkan formula matematis seperti:
\begin{equation} f(x) = \frac{e^{-x}}{(1+e^{-x})} \end{equation}Cara menambahkan gambar:
<img src="..." width="500px"></img>
2️⃣ Code : untuk menuliskan script code
💡 symbol # pada cell code berarti adalah sebuah comment. Comment pada cell code tidak akan dieksekusi
# ini merupakan cell untuk code
print("dan ini adalah cell code tempat menuliskan code python")
dan ini adalah cell code tempat menuliskan code python
Untuk run cell code: Ctrl + Alt + Enter
Ada 2 mode cell dalam notebook:
1️⃣ Command Mode
a : Menambah cell baru di atasb : Menambah cell baru di bawahd + d : Menghapus cell terpilihc : Menyalin cell terpilihv : Paste cell terpilihm : Mengubah tipe cell ke markdowny : Mengubah tipe cell ke kodeenter : enter Edit Mode2️⃣ Edit Mode (Cell Terdapat Border Biru Persegi Panjang)
Ctrl + Enter: eksekusi satu cellEsc: mengubah edit mode menjadi command modeangka = 10
angka
10
angka
10
🚀 Mari kita coba buat sebuah objek, yang berisikan nama dss ini!
dss_name = "Data Science Series: Streamlining Invoice Processing"
print(dss_name)
Data Science Series: Streamlining Invoice Processing
Sehingga dss_name dan DSS_Name dimaknai berbeda sehingga akan dianggap variabel yang berbeda pula. Maka dari itu, penamaan variable menjadi hal yang perlu diperhatikan.
## code here
dss_name == dss_name
True
# True = 11
Kode di atas mengembalikan True sebagai output. Cobalah untuk membuat variabel baru dan gunakan True sebagai namanya. kemudian lihat apa yang akan terjadi.
SyntaxError: can't assign to keyword
Sebagai catatan, True, dan juga lawannya, False termasuk ke dalam daftar kata yang dinamakan Python Keywords. Kita tidak dapat menggunakan keyword sebagai nama variabel ataupun sebagai fungsi.
Semua python keyword selain True, False, dan None adalah huruf kecil.
✨ Keywords adalah kata kunci yang sudah ditetapkan oleh Python sebagai nama yang tidak bisa dipakai baik untuk penamaan fungsi, variabel, dan lainnya. Keyword ditulis dalam lower-case (huruf kecil semua) kecuali keyword True, False, dan None. Sejauh ini (Python 3.10) keyword yang ada pada Python adalah sebagai berikut:
# cek daftar keyword
import keyword
keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
⚠️ Berikut beberapa ketentuan dalam memberikan nama variable pada Python:
1Algoritma!, $ , &, dll tidak dapat digunakan dalam penamaan variabel.algoritma, ALGORITMA, dan Algoritma adalah 3 variable yang berbedaUntuk mengecek tipe data python dapat menggunakan
type()
Python mewakili string apapun sebagai object str. Adapun beberapa cara untuk membuat nilai string:
'' (contoh: 'Jakarta' atau 'saya lahir di Bekasi')"" (contoh: "hari jum'at")''' atau """ (contoh: '''Dia bersorak "Hore! Sudah hari Jum'at"''')dss_name
'Data Science Series: Streamlining Invoice Processing'
# code here
type(dss_name)
str
Untuk menyimpan number, python memiliki dua tipe data asli yang disebut int dan float.
int digunakan untuk menyimpan bilangan bulat (yaitu: 1,2,-3)float digunakan untuk menyimpan bilangan real (yaitu: 0.7, -1.8, -1000.0) 👉🏻 bilangan desimal.nilai_ta = 100.0
nilai_fiqey = 90.0
type(nilai_ta)
float
# code here
nilai_ta - nilai_fiqey
10.0
✨ Operasi Matematika ✨
+ - Penambahan- - Pengurangan* - Perkalian/ - Pembagian// - Pembagian yang dibulatkan ke bawah% - Modulus (sisa pembagian)** - Eksponen (pangkat)Boolean hanya berisikan nilai True atau False. Biasanya untuk menunjukkan kebenaran suatu kondisi
2 + 2 == 5
# == --> untuk mengecek kondisi (apakah sama?)
False
list digunakan untuk menyimpan beberapa nilai dalam python. Untuk membuatnya, cukup letakan nilai di dalam tanda kurung siku []
# list berikut mengandung informasi nama, kota domisili, umur, apakah sudah menikah?
data_list = ['Sari', 'Jakarta', 27, False]
# 0, 1, 2, 3
# cek tipe data my_list
type(data_list)
list
# akses umur sari
data_list[2]
27
✨ Operasi List ✨
Dalam sebuah list kita dapat melakukan beberapa operasi:
list_x.append(a): Menambahkan a ke dalam list_x.list_x.remove(a): Menghapus nilai a dari list_x.Operasi Indexing:
list_x[i]: Mengakses elemen ke-i dari list_x.📌 Additional Information
# akses informasi kota domilisi dari seorang Sari, dimana informasi kota domilisi terletak di urutan ke-2 (index ke-1)
data_list[1]
'Jakarta'
# coba kita tambahkan informasi pekerjaan 'Sari', yaitu 'Data Scientist'
data_list.append('Data Scientist')
data_list
['Sari', 'Jakarta', 27, False, 'Data Scientist']
dictionary atau dict digunakan untuk menyimpan data dalam bentuk pasangan key-value. Dalam dictionary, setiap key harus unik, dan value-nya bisa berupa berbagai tipe data seperti string, integer, list, atau bahkan dictionary lainnya.
✨Dictionary didefinisikan dengan menggunakan tanda kurung kurawal {}.
# dictionary berisi informasi tentang seseorang
data_dict = {
'nama': 'Sari',
'kota_domisili': 'Jakarta',
'umur': 27,
'menikah': False
}
# code here
type(data_dict)
dict
# code here
data_dict['kota_domisili']
'Jakarta'
Dictionary memiliki keunggulan daripada list karena memungkinkan penyimpanan data dengan key yang bersifat unik, sehingga memudahkan akses dan manipulasi data berdasarkan identifikasi key, bukan hanya index angka.
int untuk bilangan bulatfloat untuk bilangan real.True atau False.[].dictionary dalam bentuk pasangan key-value dan menggunakan syntax tanda kurung kurawal {}Pernyataan if-else terdiri dari suatu kondisi yang diikuti oleh blok kode yang akan dieksekusi jika kondisi tersebut benar, dan blok else yang bersifat opsional untuk kode yang akan dieksekusi jika kondisi tersebut salah.
if kondisi:
# Kode yang akan dieksekusi jika kondisi benar
else:
# Kode yang akan dieksekusi jika kondisi salah
angka = -10
# Penggunaan if-else untuk menentukan apakah angka positif, negatif, atau nol
if angka > 0:
print("Angka ini adalah positif.")
elif angka < 0: # else -if
print("Angka ini adalah negatif.")
else:
print("Angka ini adalah nol.")
Angka ini adalah negatif.
Function merupakan sekelompok perintah yang digunakan untuk melakukan tugas tertentu. Ketika kita melakukan sesuatu yang berulang dan rumit, alangkah baiknya kita menggunakan fungsi agar tidak ada langkah yang berubah maupun penulisan kode yang salah. Penulisan umum sebuah fungsi yaitu:
def nama_fungsi(parameter):
perintah
Pada syntax umum di atas, def merupakan inisiator untuk sebuah fungsi. Sementara hal-hal yang harus kita tentukan yaitu nama dari fungsi, parameter yang akan digunakan di dalamnya, serta perintah atau kode.
Sebagai contoh, kita akan membuat sebuah fungsi luas segitiga:
# fungsi luas_segitiga
def luas_persegi(sisi): # parameter : sesuatu yang bisa kita ubah dg mendefinisikannya saat pemanggilan fungsi
hasil = sisi * sisi
return hasil # mengembalikan hasil proses dari suatu fungsi
# memanggil fungsi
result2 = luas_persegi(sisi = 4)
# result
luas_persegi(sisi=5)
25
for loop digunakan untuk mengulangi/mengiterasi suatu urutan (dapat berupa list atau string).
Contoh for loop untuk mengiterasi elemen pada list:
jabodetabek = ['Jakarta', 'Bogor', 'Depok', 'Tangerang', 'Bekasi']
# for loop
for kota in jabodetabek: # x: perwakilan nama sebuah elemen dari jabodetabek
print("kota: ",kota)
print("----")
kota: Jakarta ---- kota: Bogor ---- kota: Depok ---- kota: Tangerang ---- kota: Bekasi ----
pandas¶pandas adalah library yang powerful sebagai tools analisis data dan struktur pada Python. Library ini memiliki kemampuan sebagai berikut:
pandas mampu mengolah data menjadi mudah karena mempunyai objek bernama DataFrame.
pandas memiliki function yang mampu mengolah dataframe dengan menerapkan berbagai operasi dan teknik seperti join, agregasi, grouping, dan lain sebagainya
Lebih lengkapnya silahkan kunjungi official documentation
Untuk menggunakan pandas, kita perlu import terlebih dahulu library dengan cara berikut ini:
import pandas as pd # aliasing
print(pd.__version__)
2.1.4
pandas¶Untuk membaca data atau file dengan format .csv dapat menggunakan method .read_csv().
Syntax:
pandas.read_csv("path/data")
🚀 Bacalah data receipts.csv yang berada dalam folder data_input
receipts = pd.read_csv('data_input/receipt_cleaned.csv')
receipts
| receipt_id | item_name | count | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | real ganache | 1.0 | 16500 | 45500 |
| 1 | ID0000 | egg tart | 1.0 | 13000 | 45500 |
| 2 | ID0000 | pizza toast | 1.0 | 16000 | 45500 |
| 3 | ID0001 | kopi susu kolonel | 1.0 | 23000 | 23000 |
| 4 | ID0002 | s-ovaltine | 1.0 | 20000 | 20000 |
| 5 | ID0003 | m-carame 1 black tea | 1.0 | 28000 | 28000 |
| 6 | ID0004 | bbq chicken | 1.0 | 41000 | 41000 |
| 7 | ID0005 | le mineral | 1.0 | 8000 | 8000 |
| 8 | ID0006 | potato sausage bread | 1.0 | 19000 | 123000 |
| 9 | ID0006 | oreo green tea spread | 1.0 | 52000 | 123000 |
| 10 | ID0006 | white choco banana spread | 1.0 | 52000 | 123000 |
| 11 | ID0007 | choco devil | 4.0 | 63636 | 59500 |
| 12 | ID0007 | cp 360 club card | 1.0 | -9545 | 59500 |
| 13 | ID0008 | talam ungu | 3.0 | 19500 | 11700 |
| 14 | ID0008 | mika kecil | 1.0 | 0 | 11700 |
| 15 | ID0009 | tahu ikan oma giok | 1.0 | 20000 | 20000 |
| 16 | ID0010 | serbu | 2.0 | 40000 | 60000 |
| 17 | ID0010 | choco peanut bread | 2.0 | 20000 | 60000 |
| 18 | ID0011 | se'i sapi sambal matah ( r ) | 1.0 | 20000 | 89100 |
| 19 | ID0011 | se'i s-pi lada hitam (j) | 1.0 | 35000 | 89100 |
| 20 | ID0011 | nasi putih | 2.0 | 10000 | 89100 |
| 21 | ID0011 | milk shake coklat | 1.0 | 16000 | 89100 |
| 22 | ID0012 | es kopi susu | 4.0 | 72000 | 72000 |
| 23 | ID0013 | mineral 600 ml | 1.0 | 7727 | 45500 |
| 24 | ID0013 | bulgogi rice r | 1.0 | 33636 | 45500 |
| 25 | ID0014 | arem arem | 2.0 | 24000 | 39600 |
| 26 | ID0014 | kroket | 1.0 | 12000 | 39600 |
| 27 | ID0015 | arem arem | 2.0 | 24000 | 59400 |
| 28 | ID0015 | pepenero pastel | 2.0 | 30000 | 59400 |
| 29 | ID0016 | tt | 1.0 | 20000 | 20000 |
| 30 | ID0017 | lemonade 16oz | 1.0 | 20000 | 20000 |
| 31 | ID0018 | beef c roll 3pcs | 1.0 | 10000 | 25000 |
| 32 | ID0018 | kaya bred | 1.0 | 15000 | 25000 |
| 33 | ID0019 | futami 17 green tea (clas | 1.0 | 12500 | 42500 |
| 34 | ID0019 | egg tart | 1.0 | 13000 | 42500 |
| 35 | ID0019 | grain croque monsieur | 1.0 | 17000 | 42500 |
Deskripsi Data:
Data receipt_cleaned.csv merupakan data transaksi atau struk pembelian dari suatu tempat seperti kafe atau restoran. Berikut adalah deskripsi untuk setiap kolom dalam data:
receipt_id: Nomor identifikasi unik untuk setiap struk pembelian.item_name: Nama produk atau item yang dibeli.count: Jumlah unit dari suatu produk yang dibeli.price: Harga per unit dari suatu produk.total_price: Total harga untuk suatu produk (jumlah unit dikalikan dengan harga per unit).Lakukan pengamatan 5 data teratas dari receipts menggunakan .head()
# code here
receipts.head()
| receipt_id | item_name | count | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | real ganache | 1.0 | 16500 | 45500 |
| 1 | ID0000 | egg tart | 1.0 | 13000 | 45500 |
| 2 | ID0000 | pizza toast | 1.0 | 16000 | 45500 |
| 3 | ID0001 | kopi susu kolonel | 1.0 | 23000 | 23000 |
| 4 | ID0002 | s-ovaltine | 1.0 | 20000 | 20000 |
pandas Data Types¶pandas akan menentukan tipe data dari masing-masing Series, tapi hasil dari pandas tidak selalu benar.Berikut rangkuman tipe data pandas:
💡 Notes: Fokus pada kolom Pandas dtype dan Usage
| Pandas dtype | Python type | Usage |
|---|---|---|
| object, str | str | Text or mixed numeric and non-numeric values |
| int64 | int | Integer numbers |
| float64 | float | Floating point numbers |
| bool | bool | True/False values |
| datetime64[ns] | - | Date and time values |
| timedelta[ns] | - | Differences between two datetimes |
| category | - | Finite list of text values |
Referensi: Overview of Pandas Data Types
Tipe data Pandas:
int64: Integer (bilangan bulat, tanpa koma)float64: Bilangan desimal (berkoma)object: Text (string)category: Kategorikaldatetime64[ns]: Data waktuKarakteristik tipe data category :
Saat kita membaca data dengan pd.read_csv(), pandas akan mencoba menentukan tipe data dari setiap kolom. Lakukan investigasi awal untuk melihat struktur data terhadap object DataFrame dengan menggunakan method .info().
# cek tipe data
receipts.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 36 entries, 0 to 35 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 receipt_id 36 non-null object 1 item_name 36 non-null object 2 count 36 non-null float64 3 price 36 non-null int64 4 total_price 36 non-null int64 dtypes: float64(1), int64(2), object(2) memory usage: 1.5+ KB
💡 NOTES
Dengan menggunakan method .info(), kita dapat memeriksa informasi lengkap dari DataFrame kita:
.shape).columns).dtypes)Kolom manakah yang memiliki format tipe data yang belum sesuai?
Jawaban:
item_name-> category
Untuk mengubah tipe data pada pandas, dapat menggunakan method astype().
Syntax
df['column_name'] = df['column_name'].astype('new_data_types')
receipts['item_name'].nunique()
34
# change & check data type
receipts['item_name'].astype('category')
0 real ganache 1 egg tart 2 pizza toast 3 kopi susu kolonel 4 s-ovaltine 5 m-carame 1 black tea 6 bbq chicken 7 le mineral 8 potato sausage bread 9 oreo green tea spread 10 white choco banana spread 11 choco devil 12 cp 360 club card 13 talam ungu 14 mika kecil 15 tahu ikan oma giok 16 serbu 17 choco peanut bread 18 se'i sapi sambal matah ( r ) 19 se'i s-pi lada hitam (j) 20 nasi putih 21 milk shake coklat 22 es kopi susu 23 mineral 600 ml 24 bulgogi rice r 25 arem arem 26 kroket 27 arem arem 28 pepenero pastel 29 tt 30 lemonade 16oz 31 beef c roll 3pcs 32 kaya bred 33 futami 17 green tea (clas 34 egg tart 35 grain croque monsieur Name: item_name, dtype: category Categories (34, object): ['arem arem', 'bbq chicken', 'beef c roll 3pcs', 'bulgogi rice r', ..., 'tahu ikan oma giok', 'talam ungu', 'tt', 'white choco banana spread']
Karakteristik tipe data object:
Contoh: ID, NIK, No.HP
Karakteristik tipe data category:
Contoh: Gender, Nama Barang dalam data transaksi, provinsi, jenis kartu kredit, kategori barang: elektronik dan non elektronik
Dua alasan mengapa kita perlu menggunakan tipe data categorical:
💡 Kita bisa menggunakan method berikut untuk mengidentifikasi kolom mana yang cocok untuk disimpan ke tipe data category
.unique() untuk melihat nilai unik di setiap Series/kolom.nunique() untuk melihat jumlah nilai unik/distict pada Series/DataframeBerikut contoh syntax untuk mengecek nilai unik pada sebuah Series
df['nama_kolom'].unique()
--- START OF DAY 2 ---
Dalam analisis data, manipulasi dan wrangling data adalah langkah awal kritis yang menentukan keberhasilan analisis. Manipulasi data memungkinkan pengaturan dan filtering data menjadi format yang siap analisis, sementara wrangling data memastikan kebersihan dan keakuratan data. Kedua proses ini fundamental dalam mengubah data mentah menjadi wawasan yang berharga dan dapat diandalkan.
✨ Dalam proses pembersihan data, terutama pada kolom dengan tipe data string yang berasal dari object dalam DataFrame Pandas, operasi seperti lower(), upper(), dan replace() sering digunakan untuk menyederhanakan atau menstandarisasi teks.
receipts['item_name'].info()
<class 'pandas.core.series.Series'> RangeIndex: 36 entries, 0 to 35 Series name: item_name Non-Null Count Dtype -------------- ----- 36 non-null object dtypes: object(1) memory usage: 416.0+ bytes
🔻 .str.lower(): digunakan untuk mengubah semua karakter menjadi huruf kecil
receipts['item_name'].str.lower()
0 real ganache 1 egg tart 2 pizza toast 3 kopi susu kolonel 4 s-ovaltine 5 m-carame 1 black tea 6 bbq chicken 7 le mineral 8 potato sausage bread 9 oreo green tea spread 10 white choco banana spread 11 choco devil 12 cp 360 club card 13 talam ungu 14 mika kecil 15 tahu ikan oma giok 16 serbu 17 choco peanut bread 18 se'i sapi sambal matah ( r ) 19 se'i s-pi lada hitam (j) 20 nasi putih 21 milk shake coklat 22 es kopi susu 23 mineral 600 ml 24 bulgogi rice r 25 arem arem 26 kroket 27 arem arem 28 pepenero pastel 29 tt 30 lemonade 16oz 31 beef c roll 3pcs 32 kaya bred 33 futami 17 green tea (clas 34 egg tart 35 grain croque monsieur Name: item_name, dtype: object
# mengubah semua teks dalam kolom 'item_name' menjadi huruf kecil
receipts['item_name'].str.lower()[5]
'm-carame 1 black tea'
🔻 upper(): digunakan untuk mengubah semua karakter menjadi huruf kapital
# mengubah semua teks dalam kolom 'receipt_id' menjadi huruf besar
# pak muslim & pak anthony
receipts['receipt_id'] = receipts['receipt_id'].str.upper()
receipts['receipt_id']
0 ID0000 1 ID0000 2 ID0000 3 ID0001 4 ID0002 5 ID0003 6 ID0004 7 ID0005 8 ID0006 9 ID0006 10 ID0006 11 ID0007 12 ID0007 13 ID0008 14 ID0008 15 ID0009 16 ID0010 17 ID0010 18 ID0011 19 ID0011 20 ID0011 21 ID0011 22 ID0012 23 ID0013 24 ID0013 25 ID0014 26 ID0014 27 ID0015 28 ID0015 29 ID0016 30 ID0017 31 ID0018 32 ID0018 33 ID0019 34 ID0019 35 ID0019 Name: receipt_id, dtype: object
🔻 replace(): digunakan untuk mengganti karakter tertentu dari sebuah string
# contoh menghapus '()' dalam string
receipts['item_name'].str.replace('(', '').str.replace(')', '')
0 real ganache 1 egg tart 2 pizza toast 3 kopi susu kolonel 4 s-ovaltine 5 m-carame 1 black tea 6 bbq chicken 7 le mineral 8 potato sausage bread 9 oreo green tea spread 10 white choco banana spread 11 choco devil 12 cp 360 club card 13 talam ungu 14 mika kecil 15 tahu ikan oma giok 16 serbu 17 choco peanut bread 18 se'i sapi sambal matah r 19 se'i s-pi lada hitam j 20 nasi putih 21 milk shake coklat 22 es kopi susu 23 mineral 600 ml 24 bulgogi rice r 25 arem arem 26 kroket 27 arem arem 28 pepenero pastel 29 tt 30 lemonade 16oz 31 beef c roll 3pcs 32 kaya bred 33 futami 17 green tea clas 34 egg tart 35 grain croque monsieur Name: item_name, dtype: object
# mengganti '-' dengan ' ' (spasi) dalam kolom 'item_name'
receipts['item_name'].str.replace('-', ' ')
0 real ganache 1 egg tart 2 pizza toast 3 kopi susu kolonel 4 s ovaltine 5 m carame 1 black tea 6 bbq chicken 7 le mineral 8 potato sausage bread 9 oreo green tea spread 10 white choco banana spread 11 choco devil 12 cp 360 club card 13 talam ungu 14 mika kecil 15 tahu ikan oma giok 16 serbu 17 choco peanut bread 18 se'i sapi sambal matah ( r ) 19 se'i s pi lada hitam (j) 20 nasi putih 21 milk shake coklat 22 es kopi susu 23 mineral 600 ml 24 bulgogi rice r 25 arem arem 26 kroket 27 arem arem 28 pepenero pastel 29 tt 30 lemonade 16oz 31 beef c roll 3pcs 32 kaya bred 33 futami 17 green tea (clas 34 egg tart 35 grain croque monsieur Name: item_name, dtype: object
Untuk menghitung contigency tables, kita dapat menggunakan method .value_counts().
Kegunaan: Menghitung banyak baris pada setiap category dalam 1 kolom, dan defaultnya diurutkan secara descending
Parameter:
ascending=True: urutkan nilai dalam urutan menaik🔻 Mari kita melihat jumlah item setiap receipt yang ada
# code here
receipts['receipt_id'].value_counts()
receipt_id ID0011 4 ID0000 3 ID0006 3 ID0019 3 ID0007 2 ID0008 2 ID0013 2 ID0014 2 ID0015 2 ID0018 2 ID0010 2 ID0004 1 ID0005 1 ID0003 1 ID0009 1 ID0001 1 ID0002 1 ID0012 1 ID0016 1 ID0017 1 Name: count, dtype: int64
❓ Mari kita lihat barang apa yang sering dibeli?
# code here (pak heinz)
receipts["item_name"].value_counts(ascending=False)
item_name arem arem 2 egg tart 2 real ganache 1 se'i s-pi lada hitam (j) 1 nasi putih 1 milk shake coklat 1 es kopi susu 1 mineral 600 ml 1 bulgogi rice r 1 kroket 1 pepenero pastel 1 tt 1 lemonade 16oz 1 beef c roll 3pcs 1 kaya bred 1 futami 17 green tea (clas 1 se'i sapi sambal matah ( r ) 1 choco peanut bread 1 serbu 1 tahu ikan oma giok 1 mika kecil 1 talam ungu 1 cp 360 club card 1 choco devil 1 white choco banana spread 1 oreo green tea spread 1 potato sausage bread 1 le mineral 1 bbq chicken 1 m-carame 1 black tea 1 s-ovaltine 1 kopi susu kolonel 1 pizza toast 1 grain croque monsieur 1 Name: count, dtype: int64
Conditional subsetting bermaksud untuk mengambil sebagian data dari DataFrame berdasarkan suatu kondisi tertentu, seperti:
add_cost, atau add_cost != 0Syntax penulisan untuk conditional subsetting adalah:
df[df['column_name'] <comparison_operator> <value>]
atau
df[df.column_name <comparison_operator> <value>]
Contoh comparison_operator adalah seperti ==, !=, >, >=, <, <=.
Menampilkan data pembelian arem-arem
receipts[receipts['item_name'] == 'arem arem']
kondisi -> receipts['item_name'] == 'arem arem'
receipts[receipts['item_name'] == 'arem arem']
| receipt_id | item_name | count | price | total_price | |
|---|---|---|---|---|---|
| 25 | ID0014 | arem arem | 2.0 | 24000 | 39600 |
| 27 | ID0015 | arem arem | 2.0 | 24000 | 59400 |
❓ Menampilkan data pembelian yang memiliki receipt_id ID0015
# code here: pak husada
receipts[receipts['receipt_id'] == 'ID0015']
| receipt_id | item_name | count | price | total_price | |
|---|---|---|---|---|---|
| 27 | ID0015 | arem arem | 2.0 | 24000 | 59400 |
| 28 | ID0015 | pepenero pastel | 2.0 | 30000 | 59400 |
❓ Menampilkan data pembelian yang memiliki total pengeluaran lebih dari > Rp 50.000
# code here: pak heinz
receipt_above50k = receipts[receipts['total_price'] > 50000]
receipt_above50k
| receipt_id | item_name | count | price | total_price | |
|---|---|---|---|---|---|
| 8 | ID0006 | potato sausage bread | 1.0 | 19000 | 123000 |
| 9 | ID0006 | oreo green tea spread | 1.0 | 52000 | 123000 |
| 10 | ID0006 | white choco banana spread | 1.0 | 52000 | 123000 |
| 11 | ID0007 | choco devil | 4.0 | 63636 | 59500 |
| 12 | ID0007 | cp 360 club card | 1.0 | -9545 | 59500 |
| 16 | ID0010 | serbu | 2.0 | 40000 | 60000 |
| 17 | ID0010 | choco peanut bread | 2.0 | 20000 | 60000 |
| 18 | ID0011 | se'i sapi sambal matah ( r ) | 1.0 | 20000 | 89100 |
| 19 | ID0011 | se'i s-pi lada hitam (j) | 1.0 | 35000 | 89100 |
| 20 | ID0011 | nasi putih | 2.0 | 10000 | 89100 |
| 21 | ID0011 | milk shake coklat | 1.0 | 16000 | 89100 |
| 22 | ID0012 | es kopi susu | 4.0 | 72000 | 72000 |
| 27 | ID0015 | arem arem | 2.0 | 24000 | 59400 |
| 28 | ID0015 | pepenero pastel | 2.0 | 30000 | 59400 |
# optional: mengambil yang unique saja
receipt_above50k['receipt_id'].unique()
array(['ID0006', 'ID0007', 'ID0010', 'ID0011', 'ID0012', 'ID0015'],
dtype=object)
Dalam bisnis, kebutuhan untuk mengubah gambar menjadi teks sering muncul, yang awalnya diatasi dengan teknologi OCR (Optical Character Recognition).
Namun, OCR memiliki beberapa kelemahan, yaitu:

Seiring berkembangnya riset, muncul model OCR-free Document Understanding Transformer (Donut) yang mengintegrasikan arsitektur Transformer tanpa bergantung pada OCR, memungkinkan ekstraksi teks dari gambar dengan lebih akurat dan tanpa batasan bahasa.
Donut adalah model berbasis Transformer yang dirancang untuk tugas pemahaman dokumen yang tidak menggunakan OCR (Optical Character Recognition), menggunakan teknik mengekstrak informasi bermakna dari gambar dokumen yang tidak terstruktur.
Proses mengubah dokumen gambar menjadi output text/JSON yang dilakukan oleh model Donut melalui beberapa langkah:

Ini meningkatkan efisiensi dan akurasi dalam pengolahan dokumen, terutama untuk dokumen dengan layout kompleks.
Transformer adalah sebuah arsitektur neural network yang diperkenalkan pada tahun 2017 dalam makalah berjudul "Attention Is All You Need" oleh Ashish Vaswani dan tim peneliti dari Google AI.
Arsitektur Transformer adalah terobosan besar dalam bidang natural language processing (NLP) dan telah mengubah cara kita mendekati tugas-tugas seperti machine translation, NLP, dan banyak aplikasi lain yang melibatkan pemrosesan sequential data. Contoh model Transformer seperti BERT, GPT (Generative Pre-trained Transformer), dan T5 (Text-to-Text Transfer Transformer)
✨ State-of-The-Art Transformer: ✨
Model Donut lebih efektif daripada OCR tradisional dalam mengenali teks dari gambar dokumen, terutama yang memiliki layout kompleks atau kualitas gambar yang rendah. Donut dapat memahami konteks dan layout dokumen secara lebih akurat dan cepat.
Selain document information extraction, Donut Model dapat melakukan berbagai tugas pemrosesan dokumen lainnya, termasuk:
💡 Tak hanya itu, fakta menarik dari model Donut adalah bahwa model ini dilatih menggunakan teks dalam berbagai bahasa dengan memanfaatkan SynthDoG. SynthDoG adalah sebuah generator dokumen sintetis yang membantu pelatihan model Donut untuk fleksibel dalam berbagai bahasa dan domain.

Dengan menggunakan SynthDoG, Donut tidak hanya efektif dalam memahami dokumen dalam bahasa Inggris, tetapi juga dalam bahasa lain, memberikan kemampuan pemahaman dokumen yang lebih luas dan serbaguna.
Model Donut bisa digunakan dalam Python dengan syarat kita telah melakukan instalasi yang diperlukan dan melakukan impor library yang diperlukan.
import re # untuk cleaning menggunakan regex (regular expression)
import os # untuk pembacaan data image -> baca nama file
import torch
from PIL import Image # untuk membaca gambar
from transformers import DonutProcessor, VisionEncoderDecoderModel
💡 Notes:
DonutProcessor adalah kelas yang digunakan untuk memproses input untuk model Donut.VisionEncoderDecoderModel adalah kelas yang mengimplementasikan arsitektur model Vision Encoder-Decoder, yang dapat digunakan untuk tugas pengolahan citra dan natural language processing.🔻Mari kita load model donut ("naver-clova-ix/donut-base-finetuned-cord-v2") yang akan kita gunakan
⚠️ It will take longer if it's your first time loading it.
"cord-v2" -> untuk task document extraction
processor = DonutProcessor.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2")
model = VisionEncoderDecoderModel.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2")
Could not find image processor class in the image processor config or the model config. Loading based on pattern matching with the model's feature extractor configuration.
# set model device
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
VisionEncoderDecoderModel(
(encoder): DonutSwinModel(
(embeddings): DonutSwinEmbeddings(
(patch_embeddings): DonutSwinPatchEmbeddings(
(projection): Conv2d(3, 128, kernel_size=(4, 4), stride=(4, 4))
)
(norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(encoder): DonutSwinEncoder(
(layers): ModuleList(
(0): DonutSwinStage(
(blocks): ModuleList(
(0-1): 2 x DonutSwinLayer(
(layernorm_before): LayerNorm((128,), eps=1e-05, elementwise_affine=True)
(attention): DonutSwinAttention(
(self): DonutSwinSelfAttention(
(query): Linear(in_features=128, out_features=128, bias=True)
(key): Linear(in_features=128, out_features=128, bias=True)
(value): Linear(in_features=128, out_features=128, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(output): DonutSwinSelfOutput(
(dense): Linear(in_features=128, out_features=128, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
(drop_path): DonutSwinDropPath(p=0.1)
(layernorm_after): LayerNorm((128,), eps=1e-05, elementwise_affine=True)
(intermediate): DonutSwinIntermediate(
(dense): Linear(in_features=128, out_features=512, bias=True)
(intermediate_act_fn): GELUActivation()
)
(output): DonutSwinOutput(
(dense): Linear(in_features=512, out_features=128, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
)
(downsample): DonutSwinPatchMerging(
(reduction): Linear(in_features=512, out_features=256, bias=False)
(norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
)
)
(1): DonutSwinStage(
(blocks): ModuleList(
(0-1): 2 x DonutSwinLayer(
(layernorm_before): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(attention): DonutSwinAttention(
(self): DonutSwinSelfAttention(
(query): Linear(in_features=256, out_features=256, bias=True)
(key): Linear(in_features=256, out_features=256, bias=True)
(value): Linear(in_features=256, out_features=256, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(output): DonutSwinSelfOutput(
(dense): Linear(in_features=256, out_features=256, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
(drop_path): DonutSwinDropPath(p=0.1)
(layernorm_after): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(intermediate): DonutSwinIntermediate(
(dense): Linear(in_features=256, out_features=1024, bias=True)
(intermediate_act_fn): GELUActivation()
)
(output): DonutSwinOutput(
(dense): Linear(in_features=1024, out_features=256, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
)
(downsample): DonutSwinPatchMerging(
(reduction): Linear(in_features=1024, out_features=512, bias=False)
(norm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
)
)
(2): DonutSwinStage(
(blocks): ModuleList(
(0-13): 14 x DonutSwinLayer(
(layernorm_before): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
(attention): DonutSwinAttention(
(self): DonutSwinSelfAttention(
(query): Linear(in_features=512, out_features=512, bias=True)
(key): Linear(in_features=512, out_features=512, bias=True)
(value): Linear(in_features=512, out_features=512, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(output): DonutSwinSelfOutput(
(dense): Linear(in_features=512, out_features=512, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
(drop_path): DonutSwinDropPath(p=0.1)
(layernorm_after): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
(intermediate): DonutSwinIntermediate(
(dense): Linear(in_features=512, out_features=2048, bias=True)
(intermediate_act_fn): GELUActivation()
)
(output): DonutSwinOutput(
(dense): Linear(in_features=2048, out_features=512, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
)
(downsample): DonutSwinPatchMerging(
(reduction): Linear(in_features=2048, out_features=1024, bias=False)
(norm): LayerNorm((2048,), eps=1e-05, elementwise_affine=True)
)
)
(3): DonutSwinStage(
(blocks): ModuleList(
(0-1): 2 x DonutSwinLayer(
(layernorm_before): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
(attention): DonutSwinAttention(
(self): DonutSwinSelfAttention(
(query): Linear(in_features=1024, out_features=1024, bias=True)
(key): Linear(in_features=1024, out_features=1024, bias=True)
(value): Linear(in_features=1024, out_features=1024, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
(output): DonutSwinSelfOutput(
(dense): Linear(in_features=1024, out_features=1024, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
(drop_path): DonutSwinDropPath(p=0.1)
(layernorm_after): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
(intermediate): DonutSwinIntermediate(
(dense): Linear(in_features=1024, out_features=4096, bias=True)
(intermediate_act_fn): GELUActivation()
)
(output): DonutSwinOutput(
(dense): Linear(in_features=4096, out_features=1024, bias=True)
(dropout): Dropout(p=0.0, inplace=False)
)
)
)
)
)
)
(pooler): AdaptiveAvgPool1d(output_size=1)
)
(decoder): MBartForCausalLM(
(model): MBartDecoderWrapper(
(decoder): MBartDecoder(
(embed_tokens): Embedding(57580, 1024, padding_idx=1)
(embed_positions): MBartLearnedPositionalEmbedding(770, 1024)
(layers): ModuleList(
(0-3): 4 x MBartDecoderLayer(
(self_attn): MBartAttention(
(k_proj): Linear(in_features=1024, out_features=1024, bias=True)
(v_proj): Linear(in_features=1024, out_features=1024, bias=True)
(q_proj): Linear(in_features=1024, out_features=1024, bias=True)
(out_proj): Linear(in_features=1024, out_features=1024, bias=True)
)
(activation_fn): GELUActivation()
(self_attn_layer_norm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
(encoder_attn): MBartAttention(
(k_proj): Linear(in_features=1024, out_features=1024, bias=True)
(v_proj): Linear(in_features=1024, out_features=1024, bias=True)
(q_proj): Linear(in_features=1024, out_features=1024, bias=True)
(out_proj): Linear(in_features=1024, out_features=1024, bias=True)
)
(encoder_attn_layer_norm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
(fc1): Linear(in_features=1024, out_features=4096, bias=True)
(fc2): Linear(in_features=4096, out_features=1024, bias=True)
(final_layer_norm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
)
)
(layernorm_embedding): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
(layer_norm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
)
)
(lm_head): Linear(in_features=1024, out_features=57580, bias=False)
)
)

Tentu, untuk melakukan ekstraksi informasi dari dokumen, langkah pertama yang perlu dilakukan adalah menyiapkan gambar-gambar dokumen yang akan dibaca. Oleh karena itu, kita akan mencoba membaca beberapa file invoice yang terdapat pada folder data_input.
# folder path
dir_path = 'data_input/valid/'
img_filenames untuk menyimpan nama file dengan ekstensi .png,labels untuk menyimpan nama file dengan ekstensi .txtHasilnya, dua list tersebut berisi nama file gambar dan label teks dari dokumen gambar yang kita miliki.
Membaca file di Python umumnya melibatkan penggunaan fungsi bawaan seperti open(). Di bawah ini adalah contoh sederhana tentang cara membaca file teks dan biner di Python:
# Membuka file teks untuk dibaca (mode: 'r' untuk read)
with open('nama_file.txt', 'r') as file:
# Membaca seluruh isi file ke dalam satu string
content = file.read()
print(content)
# cara membaca suatu file di python
with open('data_input/valid/ID0000.txt') as file:
print(file.read())
{'menu': [{'nm': 'REAL GANACHE', 'cnt': '1', 'price': '16,500'}, {'nm': 'EGG TART', 'cnt': '1', 'price': '13,000'}, {'nm': 'PIZZA TOAST', 'cnt': '1', 'price': '16,000'}], 'total': {'total_price': '45,500', 'cashprice': '50,000', 'changeprice': '4,500'}}
# cara menggabungkan string di python
dir_path + 'ID0000.png'
'data_input/valid/ID0000.png'
# cara baca onjek gambar di python
Image.open('data_input/valid/ID0000.png')
img_filenames = [] # list nama file gambar
images = [] # list objek gambar
labels = [] # list label dari gambar
# iterasi direktori -> looping untuk mendapatkan seluruh nama file di `data_input/valid``
for file in os.listdir(dir_path):
if file.endswith(('.png', '.jpg', '.jpeg')): # jika file tsb berekstensi ('.png', '.jpg', '.jpeg')
# menyimpan nama gambar ke list
img_filenames.append(file)
# membuka dan menyimpan objek gambar ke list
images.append(Image.open(dir_path + file))
elif file.endswith('.txt'): # jika file tersebut berekstensi .txt
# membuka file teks dan membaca isinya, lalu menyimpan ke list
with open(dir_path + file, 'r') as label_file:
labels.append(label_file.read())
# mencetak list nama file gambar
print(img_filenames)
['ID0000.png', 'ID0001.png', 'ID0002.png', 'ID0003.png', 'ID0004.png', 'ID0005.png', 'ID0006.png', 'ID0007.png', 'ID0008.png', 'ID0009.png', 'ID0010.png', 'ID0011.png', 'ID0012.png', 'ID0013.png', 'ID0014.png', 'ID0015.png', 'ID0016.png', 'ID0017.png', 'ID0018.png', 'ID0019.png', 'ID0020.png', 'ID0021.png', 'ID0022.png', 'ID0023.png', 'ID0024.png', 'ID0025.png']
🔻 Mari coba kita lihat label dan gambar ke-7
# code here
print(labels[6])
images[6]
{'menu': [{'nm': 'POTATO SAUSAGE BREAD', 'cnt': '1', 'price': '19,000'}, {'nm': 'OREO GREEN TEA SPREAD', 'cnt': '1', 'price': '52,000'}, {'nm': 'WHITE CHOCO BANANA SPREAD', 'cnt': '1', 'price': '52,000'}], 'total': {'total_price': '123,000', 'creditcardprice': '123,000'}}
Sebagaimana umumnya pada model deep learning, diperlukan penyesuaian bentuk input sebelum dapat digunakan untuk prediksi atau pemrosesan lebih lanjut. Dalam konteks Donut Model, tahap ini melibatkan konversi gambar ke dalam format tensor dengan cara yang sederhana menggunakan alat yang disebut Donut Processor.
🎯 Proses ini diperlukan karena model, terutama yang menggunakan framework seperti PyTorch, umumnya membutuhkan input dalam bentuk tensor.
images[6].size
(864, 1296)
pixel_values = processor(images[6], return_tensors="pt").pixel_values
pixel_values.shape
torch.Size([1, 3, 1280, 960])
✨ Keterangan:
Makna ukuran pada tensor tersebut adalah [jumlah batch, jumlah channel (RGB), tinggi, lebar]. Dalam konteks ini, semua gambar diproses menjadi ukuran yang sama sebelum masuk ke model. Hal ini dilakukan karena model deep learning seringkali mensyaratkan agar seluruh input memiliki dimensi yang konsisten.
Selanjutnya, kita membiarkan model Donut secara otoregresif menghasilkan data terstruktur, mengggunakan metode .generate().
task_prompt = "<s_cord-v2>"
✨ Secara umum, model Donut dapat digunakan untuk berbagai tugas, seperti:
<s_cord-v2)
Oleh karena itu, ketika ingin menggunakan model untuk salah satu dari tugas-tugas tersebut, kita perlu menentukan task_prompt agar mengarahkan model Donut untuk fokus pada tugas spesifik yang diinginkan, sehingga output yang dihasilkan dapat lebih relevan dan bermakna sesuai dengan kebutuhan pengguna.
Selengkapnya dapat di cek di Official Github Donut
outputs = model.generate(
inputs=pixel_values.to(device), # memasukkan input
max_length=model.decoder.config.max_position_embeddings,
# parameter penting untuk set bahwa kita mau melakukan task document extraction
decoder_input_ids=processor.tokenizer(task_prompt, add_special_tokens=False, return_tensors="pt")["input_ids"].to(device),
pad_token_id=processor.tokenizer.pad_token_id, # padding
eos_token_id=processor.tokenizer.eos_token_id, # end of sentence
bad_words_ids=[[processor.tokenizer.unk_token_id]],
return_dict_in_generate=True,
use_cache=True,
# modify parameters
early_stopping=True,
num_beams=2,
output_scores=True,
)
outputs.sequences
tensor([[57579, 57526, 57528, 40769, 44398, 54165, 13663, 56992, 50990, 42990,
43547, 5909, 57527, 57530, 1314, 57529, 57532, 17888, 56239, 57531,
57522, 57528, 37040, 49429, 42990, 51290, 4004, 42990, 4008, 5176,
40099, 39415, 57527, 57530, 1314, 57529, 57532, 20017, 52066, 57531,
57522, 57528, 46193, 53278, 33938, 36277, 18081, 56701, 42990, 40266,
5178, 5176, 40099, 39415, 57527, 57530, 1314, 57529, 57532, 20017,
52066, 57531, 57525, 57544, 57546, 40474, 47108, 57545, 57564, 40474,
47108, 57563, 57543, 2]])
✨ Fungsi model.generate() pada model Donut memungkinkan untuk menghasilkan sequence text dari model. Parameter utama-utama adalah berikut
inputs : Nilai piksel dari gambar yang digunakan sebagai input untuk di-generate from doc to text.max_length: Panjang maksimum dari urutan yang dihasilkan.decoder_input_ids: ID input untuk dekoder. Di sini, tokenizer memproses task_prompt (prompt tugas yang ditentukan) dan menghasilkan ID yang relevan. add_special_tokens=False Menunjukkan bahwa token khusus tidak ditambahkan dalam proses tokenisasi.pad_token_id: ID dari token padding.eos_token_id: ID dari token akhir urutan.bad_words_ids: Daftar ID kata yang harus dihindari oleh model saat melakukan generasi.return_dict_in_generate: Mengembalikan output dalam format dictionary yang lebih mudah diakses.use_cache: Menentukan apakah hasil perhitungan sebelumnya harus disimpan untuk digunakan kembali, untuk meningkatkan efisiensi.Parameter tambahannya adalah:
early_stopping: Jika diatur True, model akan berhenti menghasilkan output lebih lanjut jika syarat tertentu terpenuhi, seperti mencapai EOS token.num_beams: Jumlah beams yang digunakan dalam pencarian beam.output_scores: Apakah mengembalikan skor skor untuk setiap token yang dihasilkan bersamaan dengan teks.📌 Untuk informasi lebih rinci, silahkan merujuk ke dokumentasi 🍩 Donut di sini.
🔻 Output hasil generate dokumen menggunakan model Donut adalah tensor yang berisi sequence token-token yang perlu dilakukan decode sehingga kita dapat mengetahui isinya. Setiap angka dalam tensor ini merepresentasikan suatu token dalam model.
# code here: check .sequences from the outputs
outputs.sequences
tensor([[57579, 57526, 57528, 40769, 44398, 54165, 13663, 56992, 50990, 42990,
43547, 5909, 57527, 57530, 1314, 57529, 57532, 17888, 56239, 57531,
57522, 57528, 37040, 49429, 42990, 51290, 4004, 42990, 4008, 5176,
40099, 39415, 57527, 57530, 1314, 57529, 57532, 20017, 52066, 57531,
57522, 57528, 46193, 53278, 33938, 36277, 18081, 56701, 42990, 40266,
5178, 5176, 40099, 39415, 57527, 57530, 1314, 57529, 57532, 20017,
52066, 57531, 57525, 57544, 57546, 40474, 47108, 57545, 57564, 40474,
47108, 57563, 57543, 2]])
🔻Maka dari itu, mari kita lakukan decode menggunakan processor yang sebelumnya juga digunakan untuk mengencode dengan menggunakan perintah processor.batch_decode()
raw_sequence = processor.batch_decode(outputs.sequences)[0]
print(raw_sequence)
<s_cord-v2><s_menu><s_nm> POTATO SAUSAGE BREAD</s_nm><s_cnt> 1</s_cnt><s_price> 19,000</s_price><sep/><s_nm> OREO GREEN TEA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price><sep/><s_nm> WHITE CHOCO BANANA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price></s_menu><s_total><s_total_price> 123,000</s_total_price><s_creditcardprice> 123,000</s_creditcardprice></s_total></s>
nm : nama item cnt : quantity price: harga total_price: total price dari struk
✨ Ini adalah teks terstruktur yang telah didekode dari tensor. Teks ini mencakup informasi terkait pesanan menu, jumlah, harga, dan total pembayaran. Strukturnya mencakup tag-tag XML (<s_cord-v2>, <s_menu>, <s_nm>, <s_cnt>, <s_price>, <sep/>, <s_total>, <s_total_price>, <s_creditcardprice>) yang memberikan konteks terhadap data yang terkandung.
🔻 Selanjutnya, dilakukan pembersihan terhadap teks. Tahap ini mencakup penghapusan token EOS (</s>), token PAD (<pad>), dan tag pertama <s_cord-v2> yang menandakan awal dari tugas pertama
sequence = raw_sequence.replace(processor.tokenizer.eos_token, "").replace(processor.tokenizer.pad_token, "") # remove </s> eos and <pad> token
sequence = re.sub(r"<.*?>", "", sequence, count=1).strip() # remove <s_cord-v2> first task start token
sequence
'<s_menu><s_nm> POTATO SAUSAGE BREAD</s_nm><s_cnt> 1</s_cnt><s_price> 19,000</s_price><sep/><s_nm> OREO GREEN TEA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price><sep/><s_nm> WHITE CHOCO BANANA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price></s_menu><s_total><s_total_price> 123,000</s_total_price><s_creditcardprice> 123,000</s_creditcardprice></s_total>'
✨ Output dari model Donut yang dihasilkan dapat kita ubah menjadi format JSON (JavaScript Object Notation), di Python mirip dengan dictionary, untuk memudahkan pengolahan dan analisis lebih lanjut. Format JSON/ dictionary memungkinkan representasi data yang terstruktur dan mudah diakses.
Kita dapat menggunakan metode token2json() untuk melakukan hal tersebut.
# code here: token to json
processor.token2json(sequence)
{'menu': [{'nm': 'POTATO SAUSAGE BREAD', 'cnt': '1', 'price': '19,000'},
{'nm': 'OREO GREEN TEA SPREAD', 'cnt': '1', 'price': '52,000'},
{'nm': 'WHITE CHOCO BANANA SPREAD', 'cnt': '1', 'price': '52,000'}],
'total': {'total_price': '123,000', 'creditcardprice': '123,000'}}
# gambar yang di generate (images[6])
images[6]
Di sini, kita akan mencapai hasil akhir dari proses generasi informasi dokumen menggunakan model Donut.
---START OF DAY 3---
💡 Untuk memudahkan dan mengotomatisasi proses ini, khususnya ketika bekerja dengan banyak gambar, kita akan menggabungkan seluruh langkah, dari 1 hingga 5, ke dalam satu fungsi. Dengan demikian, kita dapat dengan mudah menerapkan fungsi ini pada banyak gambar secara berurutan melalui looping.
def doc_to_text(input_img, task_prompt=task_prompt, model=model, processor=processor):
# set model device
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
# document preprocessing
pixel_values = processor(input_img, return_tensors="pt").pixel_values
decoder_input_ids = processor.tokenizer(task_prompt, add_special_tokens=False, return_tensors="pt")["input_ids"]
# sequence generation
outputs = model.generate(
pixel_values.to(device),
decoder_input_ids=decoder_input_ids.to(device),
max_length=model.decoder.config.max_position_embeddings,
pad_token_id=processor.tokenizer.pad_token_id,
eos_token_id=processor.tokenizer.eos_token_id,
use_cache=True,
bad_words_ids=[[processor.tokenizer.unk_token_id]],
return_dict_in_generate=True,
# modify parameters
early_stopping=True,
num_beams=2,
output_scores=True,
)
# document post-processing: sequence token cleaning
sequence = processor.batch_decode(outputs.sequences)[0]
sequence = sequence.replace(processor.tokenizer.eos_token, "").replace(processor.tokenizer.pad_token, "")
sequence = re.sub(r"<.*?>", "", sequence, count=1).strip() # remove first task start token
print(sequence)
# output conversion: token to json
output = processor.token2json(sequence)
return output
🔻 Mari kita coba men-generate 20 gambar receipt/invoice yang kita miliki!
# list untuk menyimpan hasil generate docs to text
preds = []
# code here: buatlah looping untuk predict 20 image dan disimpan di preds
# ...
for img in images[:20]:
result=doc_to_text(img)
preds.append(result)
<s_menu><s_nm> REAL GANACHE</s_nm><s_cnt> 1</s_cnt><s_price> 16,500</s_price><sep/><s_nm> EGG TART</s_nm><s_cnt> 1</s_cnt><s_price> 13,000</s_price><sep/><s_nm> PIZZA TOAST</s_nm><s_cnt> 1</s_cnt><s_price> 16,000</s_price></s_menu><s_total><s_total_price> 45,500</s_total_price><s_cashprice> 50,000</s_cashprice><s_changeprice> 4,500</s_changeprice></s_total> <s_menu><s_nm> Kopi Susu Kolonel</s_nm><s_cnt> 1</s_cnt><s_price> 23.000</s_price></s_menu><s_total><s_total_price> 23.000</s_total_price><s_cashprice> 50.000</s_cashprice><s_changeprice> 27.000</s_changeprice></s_total> <s_menu><s_nm> S-Ovaltine</s_nm><s_unitprice> 20,000</s_unitprice><s_cnt> 1</s_cnt><s_price> 20,000</s_price><s_sub><s_nm> 50%</s_nm></s_sub></s_menu><s_sub_total><s_subtotal_price> 18,181</s_subtotal_price><s_tax_price> 1,818</s_tax_price></s_sub_total><s_total><s_total_price> 20,000</s_total_price><s_cashprice> 100,000</s_cashprice><s_changeprice> 80,000</s_changeprice></s_total> <s_menu><s_nm> M-Caramel Black Tea</s_nm><s_unitprice> @28,000</s_unitprice><s_cnt> 1X</s_cnt><s_price> 28,000</s_price><s_sub><s_nm> 70%</s_nm><sep/><s_nm> Less Ice</s_nm></s_sub></s_menu><s_sub_total><s_subtotal_price> 28,000</s_subtotal_price><s_tax_price> 0.</s_tax_price></s_sub_total><s_total><s_total_price> 28,000</s_total_price><s_cashprice> 28,000</s_cashprice><s_changeprice> 0</s_changeprice></s_total> <s_menu><s_nm> BBQ Chicken</s_nm><s_cnt> 1</s_cnt><s_price> 41,000</s_price><s_sub><s_nm> Sedang</s_nm><s_cnt> 1</s_cnt><s_price> 0</s_price></s_sub></s_menu><s_sub_total><s_subtotal_price> 41,000</s_subtotal_price></s_sub_total><s_total><s_total_price> 41,000</s_total_price><s_cashprice> 50.000</s_cashprice><s_changeprice> 9,000</s_changeprice><s_menuqty_cnt> 1</s_menuqty_cnt></s_total> <s_menu><s_nm> LE MINERAL</s_nm><s_price> 8,000</s_price></s_menu><s_sub_total><s_subtotal_price> 7,273</s_subtotal_price><s_tax_price> 727</s_tax_price></s_sub_total><s_total><s_total_price> 8,000</s_total_price><s_cashprice> 8,000</s_cashprice></s_total> <s_menu><s_nm> POTATO SAUSAGE BREAD</s_nm><s_cnt> 1</s_cnt><s_price> 19,000</s_price><sep/><s_nm> OREO GREEN TEA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price><sep/><s_nm> WHITE CHOCO BANANA SPREAD</s_nm><s_cnt> 1</s_cnt><s_price> 52,000</s_price></s_menu><s_total><s_total_price> 123,000</s_total_price><s_creditcardprice> 123,000</s_creditcardprice></s_total> <s_menu><s_nm> Choco Devil</s_nm><s_cnt> 4</s_cnt><s_price> 63,636</s_price><sep/><s_nm> CP 360 Club Card</s_nm><s_price> -9,545</s_price></s_menu><s_sub_total><s_subtotal_price> 63,636</s_subtotal_price><s_discount_price> -9,545</s_discount_price><s_tax_price> 5,409</s_tax_price></s_sub_total><s_total><s_total_price> 59,500</s_total_price><s_cashprice> 100,000</s_cashprice><s_changeprice> 40,500</s_changeprice></s_total> <s_menu><s_nm> TALAM UNGU</s_nm><s_unitprice> @6500</s_unitprice><s_cnt> 3X</s_cnt><s_discountprice> -7,800</s_discountprice><s_price> 19,500</s_price><sep/><s_nm> MIKA KECIL</s_nm><s_unitprice> @0</s_unitprice><s_cnt> 1X</s_cnt><s_price> 0</s_price></s_menu><s_sub_total><s_subtotal_price> 11,700</s_subtotal_price></s_sub_total><s_total><s_total_price> 11,700</s_total_price><s_cashprice> 20,000</s_cashprice><s_changeprice> 8,300</s_changeprice><s_menuqty_cnt> 4.00xITEMs</s_menuqty_cnt></s_total> <s_menu><s_nm> Tahu Ikan Oma Giok</s_nm><s_cnt> 1</s_cnt><s_price> 20.000</s_price></s_menu><s_total><s_total_price> 20.000</s_total_price><s_cashprice> 20.000</s_cashprice><s_changeprice> 0</s_changeprice></s_total> <s_menu><s_nm> Serbu</s_nm><s_cnt> 2</s_cnt><s_price> 40.000</s_price><sep/><s_nm> Choco Peanut Bread</s_nm><s_cnt> 2</s_cnt><s_price> 20.000</s_price></s_menu><s_total><s_total_price> 60.000</s_total_price><s_cashprice> 60.000</s_cashprice><s_changeprice> 0</s_changeprice></s_total> <s_menu><s_nm> Se'I Sapi Sambal Matah ( R )</s_nm><s_cnt> 1</s_cnt><s_price> 20.000</s_price><sep/><s_nm> Se'I S-pi Lada Hitam (J)</s_nm><s_cnt> 1</s_cnt><s_price> 35.000</s_price><sep/><s_nm> Nasi Putih</s_nm><s_cnt> 2</s_cnt><s_price> 10.000</s_price><sep/><s_nm> Milk Shake Coklat</s_nm><s_cnt> 1</s_cnt><s_price> 16.000</s_price></s_menu><s_sub_total><s_subtotal_price> 81.000</s_subtotal_price><s_tax_price> 8.100</s_tax_price></s_sub_total><s_total><s_total_price> 89.100</s_total_price><s_changeprice> 0</s_changeprice><s_creditcardprice> 89.100</s_creditcardprice></s_total> <s_menu><s_nm> ES KOPI SUSU</s_nm><s_cnt> 4</s_cnt><s_price> 72.000</s_price></s_menu><s_total><s_total_price> 72.000</s_total_price><s_changeprice> 0</s_changeprice><s_emoneyprice> 72.000</s_emoneyprice></s_total> <s_menu><s_nm> MINERAL 600 ML</s_nm><s_cnt> 1</s_cnt><s_price> 7,727</s_price><sep/><s_nm> BULGOGI RICE R</s_nm><s_cnt> 1</s_cnt><s_price> 33,636</s_price></s_menu><s_sub_total><s_subtotal_price> 41,364</s_subtotal_price><s_tax_price> 4,136</s_tax_price></s_sub_total><s_total><s_total_price> 45,500</s_total_price><s_cashprice> 50,000</s_cashprice><s_changeprice> -4,500</s_changeprice></s_total> <s_menu><s_nm> Arem Arem</s_nm><s_unitprice> @12.000</s_unitprice><s_cnt> 2x</s_cnt><s_price> 24.000</s_price><sep/><s_nm> Kroket</s_nm><s_unitprice> @12.000</s_unitprice><s_cnt> 1x</s_cnt><s_price> 12.000</s_price></s_menu><s_sub_total><s_subtotal_price> Rp 36.000</s_subtotal_price><s_tax_price> Rp 3.600</s_tax_price></s_sub_total><s_total><s_total_price> Rp 39.600</s_total_price><s_emoneyprice> Rp 39.600</s_emoneyprice></s_total> <s_menu><s_nm> Arem Arem</s_nm><s_unitprice> 12.000</s_unitprice><s_cnt> 2</s_cnt><s_price> 24.000</s_price><sep/><s_nm> Pepenero Pastel</s_nm><s_unitprice> 15.000</s_unitprice><s_cnt> 2</s_cnt><s_price> 30.000</s_price></s_menu><s_sub_total><s_subtotal_price> Rp 54.000</s_subtotal_price><s_tax_price> Rp 5.400</s_tax_price></s_sub_total><s_total><s_total_price> Rp 59.400</s_total_price><s_cashprice> Rp 100.000</s_cashprice><s_changeprice> Rp 40.600</s_changeprice></s_total> <s_menu><s_nm> TT</s_nm><s_unitprice> 20,000</s_unitprice><s_cnt> 1</s_cnt><s_price> 20,000</s_price></s_menu><s_total><s_total_price> 20,000</s_total_price><s_cashprice> 100,000</s_cashprice><s_changeprice> 80,000</s_changeprice></s_total> <s_menu><s_nm> LEMONADE 16OZ</s_nm><s_unitprice> 20,000</s_unitprice><s_cnt> 1 x</s_cnt><s_price> 20,000</s_price></s_menu><s_sub_total><s_subtotal_price> 20,000</s_subtotal_price></s_sub_total><s_total><s_total_price> 20,000</s_total_price><s_cashprice> 100,000</s_cashprice><s_changeprice> 80,000</s_changeprice></s_total> <s_menu><s_nm> beef.c roll 3pcs</s_nm><s_unitprice> 10,000</s_unitprice><s_cnt> 1</s_cnt><s_price> 10,000</s_price><sep/><s_nm> kaya bred</s_nm><s_unitprice> 15,000</s_unitprice><s_cnt> 1</s_cnt><s_price> 15,000</s_price></s_menu><s_total><s_total_price> 25,000</s_total_price><s_cashprice> 100,000</s_cashprice><s_changeprice> 75,000</s_changeprice><s_menuqty_cnt> 2</s_menuqty_cnt></s_total> <s_menu><s_nm> FUTAMI 17 GREEN TEA (CLASS</s_nm><s_cnt> 1</s_cnt><s_price> 12,500</s_price><sep/><s_nm> EGG TART</s_nm><s_cnt> 1</s_cnt><s_price> 13,000</s_price><sep/><s_nm> GRAIN CROQUE MONSIEUR</s_nm><s_cnt> 1</s_cnt><s_price> 17,000</s_price></s_menu><s_total><s_total_price> 42,500</s_total_price><s_cashprice> 50,000</s_cashprice><s_changeprice> 7,500</s_changeprice></s_total>
# code here: cek hasil prediksinya
preds[0]
{'menu': [{'nm': 'REAL GANACHE', 'cnt': '1', 'price': '16,500'},
{'nm': 'EGG TART', 'cnt': '1', 'price': '13,000'},
{'nm': 'PIZZA TOAST', 'cnt': '1', 'price': '16,000'}],
'total': {'total_price': '45,500',
'cashprice': '50,000',
'changeprice': '4,500'}}
Agar mempermudah dalam kita menganalisis hasil model Donut maka kita perlu mengubah data generated text tersebut menjadi sebuah tabel.
pandas¶Kita cukup menggunakan pd.DataFrame.from_dict(), pandas secara otomatis mengonversi struktur JSON tersebut ke dalam bentuk tabel.
df_preds = pd.DataFrame.from_dict(preds)
df_preds
| menu | total | sub_total | |
|---|---|---|---|
| 0 | [{'nm': 'REAL GANACHE', 'cnt': '1', 'price': '... | {'total_price': '45,500', 'cashprice': '50,000... | NaN |
| 1 | {'nm': 'Kopi Susu Kolonel', 'cnt': '1', 'price... | {'total_price': '23.000', 'cashprice': '50.000... | NaN |
| 2 | {'nm': 'S-Ovaltine', 'unitprice': '20,000', 'c... | {'total_price': '20,000', 'cashprice': '100,00... | {'subtotal_price': '18,181', 'tax_price': '1,8... |
| 3 | {'nm': 'M-Caramel Black Tea', 'unitprice': '@2... | {'total_price': '28,000', 'cashprice': '28,000... | {'subtotal_price': '28,000', 'tax_price': '0.'} |
| 4 | {'nm': 'BBQ Chicken', 'cnt': '1', 'price': '41... | {'total_price': '41,000', 'cashprice': '50.000... | {'subtotal_price': '41,000'} |
| 5 | {'nm': 'LE MINERAL', 'price': '8,000'} | {'total_price': '8,000', 'cashprice': '8,000'} | {'subtotal_price': '7,273', 'tax_price': '727'} |
| 6 | [{'nm': 'POTATO SAUSAGE BREAD', 'cnt': '1', 'p... | {'total_price': '123,000', 'creditcardprice': ... | NaN |
| 7 | [{'nm': 'Choco Devil', 'cnt': '4', 'price': '6... | {'total_price': '59,500', 'cashprice': '100,00... | {'subtotal_price': '63,636', 'discount_price':... |
| 8 | [{'nm': 'TALAM UNGU', 'unitprice': '@6500', 'c... | {'total_price': '11,700', 'cashprice': '20,000... | {'subtotal_price': '11,700'} |
| 9 | {'nm': 'Tahu Ikan Oma Giok', 'cnt': '1', 'pric... | {'total_price': '20.000', 'cashprice': '20.000... | NaN |
| 10 | [{'nm': 'Serbu', 'cnt': '2', 'price': '40.000'... | {'total_price': '60.000', 'cashprice': '60.000... | NaN |
| 11 | [{'nm': 'Se'I Sapi Sambal Matah ( R )', 'cnt':... | {'total_price': '89.100', 'changeprice': '0', ... | {'subtotal_price': '81.000', 'tax_price': '8.1... |
| 12 | {'nm': 'ES KOPI SUSU', 'cnt': '4', 'price': '7... | {'total_price': '72.000', 'changeprice': '0', ... | NaN |
| 13 | [{'nm': 'MINERAL 600 ML', 'cnt': '1', 'price':... | {'total_price': '45,500', 'cashprice': '50,000... | {'subtotal_price': '41,364', 'tax_price': '4,1... |
| 14 | [{'nm': 'Arem Arem', 'unitprice': '@12.000', '... | {'total_price': 'Rp 39.600', 'emoneyprice': 'R... | {'subtotal_price': 'Rp 36.000', 'tax_price': '... |
| 15 | [{'nm': 'Arem Arem', 'unitprice': '12.000', 'c... | {'total_price': 'Rp 59.400', 'cashprice': 'Rp ... | {'subtotal_price': 'Rp 54.000', 'tax_price': '... |
| 16 | {'nm': 'TT', 'unitprice': '20,000', 'cnt': '1'... | {'total_price': '20,000', 'cashprice': '100,00... | NaN |
| 17 | {'nm': 'LEMONADE 16OZ', 'unitprice': '20,000',... | {'total_price': '20,000', 'cashprice': '100,00... | {'subtotal_price': '20,000'} |
| 18 | [{'nm': 'beef.c roll 3pcs', 'unitprice': '10,0... | {'total_price': '25,000', 'cashprice': '100,00... | NaN |
| 19 | [{'nm': 'FUTAMI 17 GREEN TEA (CLASS', 'cnt': '... | {'total_price': '42,500', 'cashprice': '50,000... | NaN |
📈 Insight: Setiap baris pada dataframe kita mewakili ...
🔻 Mari kita cek missing value-nya menggunakan metode isna().sum()
# code here
df_preds.isna().sum()
menu 0 total 0 sub_total 9 dtype: int64
📈 Insight: Dalam kasus kali ini kolom sub_total akan diabaikan karena kita akan mencoba mengambil total harga keseluruhan, harga per item, dan jumlah per item saja. Maka dari itu, kita tidak perlu membuat data yang subtotalnya kosong (NaN)
❓ Apakah menurut Anda, bentuk dataframe data invoice kita sudah cukup rapi/mudah dianalisis?
Kita memiliki data struk yang belum terorganisir dengan baik, dimana informasi menu, total, dan sub-total disajikan dalam satu baris.
🔻 Untuk memudahkan analisis, kita akan memecah data ini sehingga setiap item pada struk diwakili dalam baris tersendiri, lengkap dengan ID struknya. Pendekatan ini akan mempermudah pengolahan dan analisis data.
df = pd.DataFrame(columns=['receipt_id', 'nm', 'cnt', 'price', 'total_price'])
df
| receipt_id | nm | cnt | price | total_price |
|---|
# cara mengecek jumlah baris pada suatu dataframe
len(df)
0
df_preds ke df dengan memanfaatkan looping .iterrows().💡 .iterrows() merupakan metode pada objek DataFrame yang menghasilkan pasangan (index, row) untuk DataFrame tersebut
for index,row in df_preds.iterrows():
print(index,row)
0 menu [{'nm': 'REAL GANACHE', 'cnt': '1', 'price': '...
total {'total_price': '45,500', 'cashprice': '50,000...
sub_total NaN
Name: 0, dtype: object
1 menu {'nm': 'Kopi Susu Kolonel', 'cnt': '1', 'price...
total {'total_price': '23.000', 'cashprice': '50.000...
sub_total NaN
Name: 1, dtype: object
2 menu {'nm': 'S-Ovaltine', 'unitprice': '20,000', 'c...
total {'total_price': '20,000', 'cashprice': '100,00...
sub_total {'subtotal_price': '18,181', 'tax_price': '1,8...
Name: 2, dtype: object
3 menu {'nm': 'M-Caramel Black Tea', 'unitprice': '@2...
total {'total_price': '28,000', 'cashprice': '28,000...
sub_total {'subtotal_price': '28,000', 'tax_price': '0.'}
Name: 3, dtype: object
4 menu {'nm': 'BBQ Chicken', 'cnt': '1', 'price': '41...
total {'total_price': '41,000', 'cashprice': '50.000...
sub_total {'subtotal_price': '41,000'}
Name: 4, dtype: object
5 menu {'nm': 'LE MINERAL', 'price': '8,000'}
total {'total_price': '8,000', 'cashprice': '8,000'}
sub_total {'subtotal_price': '7,273', 'tax_price': '727'}
Name: 5, dtype: object
6 menu [{'nm': 'POTATO SAUSAGE BREAD', 'cnt': '1', 'p...
total {'total_price': '123,000', 'creditcardprice': ...
sub_total NaN
Name: 6, dtype: object
7 menu [{'nm': 'Choco Devil', 'cnt': '4', 'price': '6...
total {'total_price': '59,500', 'cashprice': '100,00...
sub_total {'subtotal_price': '63,636', 'discount_price':...
Name: 7, dtype: object
8 menu [{'nm': 'TALAM UNGU', 'unitprice': '@6500', 'c...
total {'total_price': '11,700', 'cashprice': '20,000...
sub_total {'subtotal_price': '11,700'}
Name: 8, dtype: object
9 menu {'nm': 'Tahu Ikan Oma Giok', 'cnt': '1', 'pric...
total {'total_price': '20.000', 'cashprice': '20.000...
sub_total NaN
Name: 9, dtype: object
10 menu [{'nm': 'Serbu', 'cnt': '2', 'price': '40.000'...
total {'total_price': '60.000', 'cashprice': '60.000...
sub_total NaN
Name: 10, dtype: object
11 menu [{'nm': 'Se'I Sapi Sambal Matah ( R )', 'cnt':...
total {'total_price': '89.100', 'changeprice': '0', ...
sub_total {'subtotal_price': '81.000', 'tax_price': '8.1...
Name: 11, dtype: object
12 menu {'nm': 'ES KOPI SUSU', 'cnt': '4', 'price': '7...
total {'total_price': '72.000', 'changeprice': '0', ...
sub_total NaN
Name: 12, dtype: object
13 menu [{'nm': 'MINERAL 600 ML', 'cnt': '1', 'price':...
total {'total_price': '45,500', 'cashprice': '50,000...
sub_total {'subtotal_price': '41,364', 'tax_price': '4,1...
Name: 13, dtype: object
14 menu [{'nm': 'Arem Arem', 'unitprice': '@12.000', '...
total {'total_price': 'Rp 39.600', 'emoneyprice': 'R...
sub_total {'subtotal_price': 'Rp 36.000', 'tax_price': '...
Name: 14, dtype: object
15 menu [{'nm': 'Arem Arem', 'unitprice': '12.000', 'c...
total {'total_price': 'Rp 59.400', 'cashprice': 'Rp ...
sub_total {'subtotal_price': 'Rp 54.000', 'tax_price': '...
Name: 15, dtype: object
16 menu {'nm': 'TT', 'unitprice': '20,000', 'cnt': '1'...
total {'total_price': '20,000', 'cashprice': '100,00...
sub_total NaN
Name: 16, dtype: object
17 menu {'nm': 'LEMONADE 16OZ', 'unitprice': '20,000',...
total {'total_price': '20,000', 'cashprice': '100,00...
sub_total {'subtotal_price': '20,000'}
Name: 17, dtype: object
18 menu [{'nm': 'beef.c roll 3pcs', 'unitprice': '10,0...
total {'total_price': '25,000', 'cashprice': '100,00...
sub_total NaN
Name: 18, dtype: object
19 menu [{'nm': 'FUTAMI 17 GREEN TEA (CLASS', 'cnt': '...
total {'total_price': '42,500', 'cashprice': '50,000...
sub_total NaN
Name: 19, dtype: object
# mengiterasi setiap rrow dalam df_preds
for index, row in df_preds.iterrows():
# mengecek apakah field 'menu' adalah list, jika tidak, ubah menjadi list
menus = row['menu'] if isinstance(row['menu'], list) else [row['menu']]
# mengiterasi setiap elemen dalam list 'menus'
for menu in menus:
# menambahkan row baru ke 'df' dengan informasi dari 'menu' serta tambahan 'total_price' dan 'receipt_id'
df.loc[len(df)] = {
**menu, # unpack semua pasangan key-value dari dictionary 'menu' yang cocok dg nama kolom yg ada
'total_price': row['total']['total_price'], # mengambil 'total_price' dari baris saat ini
'receipt_id': img_filenames[index].split('.')[0] # mengambil ID struk dari nama file gambar
}
💡**dict : Melakukan unpack semua pasangan key-value dari dictionary 'dict'
💡df.loc[] : Metode yang digunakan untuk mengakses baris atau kolom tertentu dari DataFrame berdasarkan nama/labelnya
df
| receipt_id | nm | cnt | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | REAL GANACHE | 1 | 16,500 | 45,500 |
| 1 | ID0000 | EGG TART | 1 | 13,000 | 45,500 |
| 2 | ID0000 | PIZZA TOAST | 1 | 16,000 | 45,500 |
| 3 | ID0001 | Kopi Susu Kolonel | 1 | 23.000 | 23.000 |
| 4 | ID0002 | S-Ovaltine | 1 | 20,000 | 20,000 |
| 5 | ID0003 | M-Caramel Black Tea | 1X | 28,000 | 28,000 |
| 6 | ID0004 | BBQ Chicken | 1 | 41,000 | 41,000 |
| 7 | ID0005 | LE MINERAL | NaN | 8,000 | 8,000 |
| 8 | ID0006 | POTATO SAUSAGE BREAD | 1 | 19,000 | 123,000 |
| 9 | ID0006 | OREO GREEN TEA SPREAD | 1 | 52,000 | 123,000 |
| 10 | ID0006 | WHITE CHOCO BANANA SPREAD | 1 | 52,000 | 123,000 |
| 11 | ID0007 | Choco Devil | 4 | 63,636 | 59,500 |
| 12 | ID0007 | CP 360 Club Card | NaN | -9,545 | 59,500 |
| 13 | ID0008 | TALAM UNGU | 3X | 19,500 | 11,700 |
| 14 | ID0008 | MIKA KECIL | 1X | 0 | 11,700 |
| 15 | ID0009 | Tahu Ikan Oma Giok | 1 | 20.000 | 20.000 |
| 16 | ID0010 | Serbu | 2 | 40.000 | 60.000 |
| 17 | ID0010 | Choco Peanut Bread | 2 | 20.000 | 60.000 |
| 18 | ID0011 | Se'I Sapi Sambal Matah ( R ) | 1 | 20.000 | 89.100 |
| 19 | ID0011 | Se'I S-pi Lada Hitam (J) | 1 | 35.000 | 89.100 |
| 20 | ID0011 | Nasi Putih | 2 | 10.000 | 89.100 |
| 21 | ID0011 | Milk Shake Coklat | 1 | 16.000 | 89.100 |
| 22 | ID0012 | ES KOPI SUSU | 4 | 72.000 | 72.000 |
| 23 | ID0013 | MINERAL 600 ML | 1 | 7,727 | 45,500 |
| 24 | ID0013 | BULGOGI RICE R | 1 | 33,636 | 45,500 |
| 25 | ID0014 | Arem Arem | 2x | 24.000 | Rp 39.600 |
| 26 | ID0014 | Kroket | 1x | 12.000 | Rp 39.600 |
| 27 | ID0015 | Arem Arem | 2 | 24.000 | Rp 59.400 |
| 28 | ID0015 | Pepenero Pastel | 2 | 30.000 | Rp 59.400 |
| 29 | ID0016 | TT | 1 | 20,000 | 20,000 |
| 30 | ID0017 | LEMONADE 16OZ | 1 x | 20,000 | 20,000 |
| 31 | ID0018 | beef.c roll 3pcs | 1 | 10,000 | 25,000 |
| 32 | ID0018 | kaya bred | 1 | 15,000 | 25,000 |
| 33 | ID0019 | FUTAMI 17 GREEN TEA (CLASS | 1 | 12,500 | 42,500 |
| 34 | ID0019 | EGG TART | 1 | 13,000 | 42,500 |
| 35 | ID0019 | GRAIN CROQUE MONSIEUR | 1 | 17,000 | 42,500 |
📈 Insight: ...
🔻 Mari kita simpan data document extracted yang sudah kita lakukan agar terecord dengan baik
# menyimpan DataFrame 'df' ke dalam file csv
df.to_csv('receipt_extracted.csv',
index=False) # berarti indeks DataFrame tidak disertakan dalam file csv
# cek tipe data
df.info()
<class 'pandas.core.frame.DataFrame'> Index: 36 entries, 0 to 35 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 receipt_id 36 non-null object 1 nm 36 non-null object 2 cnt 34 non-null object 3 price 36 non-null object 4 total_price 36 non-null object dtypes: object(5) memory usage: 1.7+ KB
🔻Nama kolom masih kurang informatif, maka kita akan coba menggantinya menggunakan metode .rename()
new_columns_name = {
'nm': 'item_name',
'cnt': 'quantity'
}
df = df.rename(columns=new_columns_name)
df.head()
| receipt_id | item_name | quantity | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | REAL GANACHE | 1 | 16,500 | 45,500 |
| 1 | ID0000 | EGG TART | 1 | 13,000 | 45,500 |
| 2 | ID0000 | PIZZA TOAST | 1 | 16,000 | 45,500 |
| 3 | ID0001 | Kopi Susu Kolonel | 1 | 23.000 | 23.000 |
| 4 | ID0002 | S-Ovaltine | 1 | 20,000 | 20,000 |
🔻 Jika kita lihat kembali kolom price maupun total_price seharusnya memiliki tipe data int/float. Maka dari itu kita perlu melakukan pembersihan ,.
# fungsi untuk membersihkan string harga dari simbol dan spasi
def clean_price(x):
return int(x.replace(".", "").replace(",", "").replace("Rp", "").replace(" ", ""))
Mari kita coba aplikasikan fungsi clean_price terhadap salah satu baris pada data kita
# code here
clean_price(df.loc[0,"price"])
16500
❓ Lalu, bagaimana cara mengaplikasikan fungsi tersebut untuk semua baris tanpa kita melakukan satu persatu?
Jawabannya, kita bisa memanfaatkan fungsi
.apply()
💡 Fungsi apply memungkinkan kita untuk menjalankan suatu fungsi terhadap setiap elemen dalam sebuah kolom secara otomatis. Contohnya, jika kita ingin membersihkan format harga dalam kolom 'price' di DataFrame df, kita bisa melakukan hal berikut:
# mengaplikasikan fungsi 'clean_price' ke setiap elemen di kolom 'price' pada 'df'
df['price'] = df['price'].apply(lambda x: clean_price(str(x)))
# code here: mengaplikasikan fungsi 'clean_price' ke setiap elemen di kolom 'total_price' pada 'df'
df['total_price'] = df['total_price'].apply(lambda x: clean_price(str(x)))
df.head()
| receipt_id | item_name | quantity | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | REAL GANACHE | 1 | 16500 | 45500 |
| 1 | ID0000 | EGG TART | 1 | 13000 | 45500 |
| 2 | ID0000 | PIZZA TOAST | 1 | 16000 | 45500 |
| 3 | ID0001 | Kopi Susu Kolonel | 1 | 23000 | 23000 |
| 4 | ID0002 | S-Ovaltine | 1 | 20000 | 20000 |
🔻 Selanjutnya, kita akan coba membersihkan kolom quantity seharusnya memiliki tipe data int/float. Maka dari itu kita perlu melakukan pembersihan ,.
, serta X
Mari kita buat custom fungsinya terlebih dahulu:
df.info()
<class 'pandas.core.frame.DataFrame'> Index: 36 entries, 0 to 35 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 receipt_id 36 non-null object 1 item_name 36 non-null object 2 quantity 34 non-null object 3 price 36 non-null int64 4 total_price 36 non-null int64 dtypes: int64(2), object(3) memory usage: 2.7+ KB
# membersihkan dan mengkonversi nilai 'x' menjadi float
def clean_quantity(x):
# jika 'x' bernilai None (tidak ada data), kembalikan 0
if x is None:
return 0
# mengonversi 'x' ke string, menghapus spasi, 'x'/'X', dan ']' dari string tersebut, dan mengembalikan menjadi sebuah angka
return float(str(x).replace(" ", "").replace("x", "").replace("X", "").replace("]", ""))
# code here: mengaplikasikan fungsi 'clean_quantity' ke setiap elemen di kolom 'quantity' pada 'df'
df['quantity'] = df['quantity'].apply(lambda x: clean_quantity(str(x)))
df.head()
| receipt_id | item_name | quantity | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | REAL GANACHE | 1.0 | 16500 | 45500 |
| 1 | ID0000 | EGG TART | 1.0 | 13000 | 45500 |
| 2 | ID0000 | PIZZA TOAST | 1.0 | 16000 | 45500 |
| 3 | ID0001 | Kopi Susu Kolonel | 1.0 | 23000 | 23000 |
| 4 | ID0002 | S-Ovaltine | 1.0 | 20000 | 20000 |
df
| receipt_id | item_name | quantity | price | total_price | |
|---|---|---|---|---|---|
| 0 | ID0000 | REAL GANACHE | 1.0 | 16500 | 45500 |
| 1 | ID0000 | EGG TART | 1.0 | 13000 | 45500 |
| 2 | ID0000 | PIZZA TOAST | 1.0 | 16000 | 45500 |
| 3 | ID0001 | Kopi Susu Kolonel | 1.0 | 23000 | 23000 |
| 4 | ID0002 | S-Ovaltine | 1.0 | 20000 | 20000 |
| 5 | ID0003 | M-Caramel Black Tea | 1.0 | 28000 | 28000 |
| 6 | ID0004 | BBQ Chicken | 1.0 | 41000 | 41000 |
| 7 | ID0005 | LE MINERAL | NaN | 8000 | 8000 |
| 8 | ID0006 | POTATO SAUSAGE BREAD | 1.0 | 19000 | 123000 |
| 9 | ID0006 | OREO GREEN TEA SPREAD | 1.0 | 52000 | 123000 |
| 10 | ID0006 | WHITE CHOCO BANANA SPREAD | 1.0 | 52000 | 123000 |
| 11 | ID0007 | Choco Devil | 4.0 | 63636 | 59500 |
| 12 | ID0007 | CP 360 Club Card | NaN | -9545 | 59500 |
| 13 | ID0008 | TALAM UNGU | 3.0 | 19500 | 11700 |
| 14 | ID0008 | MIKA KECIL | 1.0 | 0 | 11700 |
| 15 | ID0009 | Tahu Ikan Oma Giok | 1.0 | 20000 | 20000 |
| 16 | ID0010 | Serbu | 2.0 | 40000 | 60000 |
| 17 | ID0010 | Choco Peanut Bread | 2.0 | 20000 | 60000 |
| 18 | ID0011 | Se'I Sapi Sambal Matah ( R ) | 1.0 | 20000 | 89100 |
| 19 | ID0011 | Se'I S-pi Lada Hitam (J) | 1.0 | 35000 | 89100 |
| 20 | ID0011 | Nasi Putih | 2.0 | 10000 | 89100 |
| 21 | ID0011 | Milk Shake Coklat | 1.0 | 16000 | 89100 |
| 22 | ID0012 | ES KOPI SUSU | 4.0 | 72000 | 72000 |
| 23 | ID0013 | MINERAL 600 ML | 1.0 | 7727 | 45500 |
| 24 | ID0013 | BULGOGI RICE R | 1.0 | 33636 | 45500 |
| 25 | ID0014 | Arem Arem | 2.0 | 24000 | 39600 |
| 26 | ID0014 | Kroket | 1.0 | 12000 | 39600 |
| 27 | ID0015 | Arem Arem | 2.0 | 24000 | 59400 |
| 28 | ID0015 | Pepenero Pastel | 2.0 | 30000 | 59400 |
| 29 | ID0016 | TT | 1.0 | 20000 | 20000 |
| 30 | ID0017 | LEMONADE 16OZ | 1.0 | 20000 | 20000 |
| 31 | ID0018 | beef.c roll 3pcs | 1.0 | 10000 | 25000 |
| 32 | ID0018 | kaya bred | 1.0 | 15000 | 25000 |
| 33 | ID0019 | FUTAMI 17 GREEN TEA (CLASS | 1.0 | 12500 | 42500 |
| 34 | ID0019 | EGG TART | 1.0 | 13000 | 42500 |
| 35 | ID0019 | GRAIN CROQUE MONSIEUR | 1.0 | 17000 | 42500 |
Misalkan Anda bekerja sebagai data scientist di sebuah supermarket dengan dataset yang mencatat transaksi penjualannya. Dataset ini berisi informasi tentang ID struk (receipt_id), harga total per struk (total_price), dan nama item yang terjual (item_name).
Untuk mendapatkan wawasan yang lebih terstruktur dari data penjualan ini, Anda diminta membuat tabel pivot yang mengelompokkan dan menyajikan data berdasarkan receipt_id, total_price.
# code here
df_pivot = df.pivot_table(index=['receipt_id', 'total_price', 'item_name'])
df_pivot
| price | quantity | |||
|---|---|---|---|---|
| receipt_id | total_price | item_name | ||
| ID0000 | 45500 | EGG TART | 13000.0 | 1.0 |
| PIZZA TOAST | 16000.0 | 1.0 | ||
| REAL GANACHE | 16500.0 | 1.0 | ||
| ID0001 | 23000 | Kopi Susu Kolonel | 23000.0 | 1.0 |
| ID0002 | 20000 | S-Ovaltine | 20000.0 | 1.0 |
| ID0003 | 28000 | M-Caramel Black Tea | 28000.0 | 1.0 |
| ID0004 | 41000 | BBQ Chicken | 41000.0 | 1.0 |
| ID0005 | 8000 | LE MINERAL | 8000.0 | NaN |
| ID0006 | 123000 | OREO GREEN TEA SPREAD | 52000.0 | 1.0 |
| POTATO SAUSAGE BREAD | 19000.0 | 1.0 | ||
| WHITE CHOCO BANANA SPREAD | 52000.0 | 1.0 | ||
| ID0007 | 59500 | CP 360 Club Card | -9545.0 | NaN |
| Choco Devil | 63636.0 | 4.0 | ||
| ID0008 | 11700 | MIKA KECIL | 0.0 | 1.0 |
| TALAM UNGU | 19500.0 | 3.0 | ||
| ID0009 | 20000 | Tahu Ikan Oma Giok | 20000.0 | 1.0 |
| ID0010 | 60000 | Choco Peanut Bread | 20000.0 | 2.0 |
| Serbu | 40000.0 | 2.0 | ||
| ID0011 | 89100 | Milk Shake Coklat | 16000.0 | 1.0 |
| Nasi Putih | 10000.0 | 2.0 | ||
| Se'I S-pi Lada Hitam (J) | 35000.0 | 1.0 | ||
| Se'I Sapi Sambal Matah ( R ) | 20000.0 | 1.0 | ||
| ID0012 | 72000 | ES KOPI SUSU | 72000.0 | 4.0 |
| ID0013 | 45500 | BULGOGI RICE R | 33636.0 | 1.0 |
| MINERAL 600 ML | 7727.0 | 1.0 | ||
| ID0014 | 39600 | Arem Arem | 24000.0 | 2.0 |
| Kroket | 12000.0 | 1.0 | ||
| ID0015 | 59400 | Arem Arem | 24000.0 | 2.0 |
| Pepenero Pastel | 30000.0 | 2.0 | ||
| ID0016 | 20000 | TT | 20000.0 | 1.0 |
| ID0017 | 20000 | LEMONADE 16OZ | 20000.0 | 1.0 |
| ID0018 | 25000 | beef.c roll 3pcs | 10000.0 | 1.0 |
| kaya bred | 15000.0 | 1.0 | ||
| ID0019 | 42500 | EGG TART | 13000.0 | 1.0 |
| FUTAMI 17 GREEN TEA (CLASS | 12500.0 | 1.0 | ||
| GRAIN CROQUE MONSIEUR | 17000.0 | 1.0 |
🛠️ Gradio adalah sebuah library Python yang digunakan untuk membangun antarmuka web yang mudah digunakan untuk aplikasi berbasis machine learning. Dengan Gradio, kita dapat dengan cepat membuat antarmuka untuk model machine learning sehingga pengguna dapat dengan mudah berinteraksi dengannya tanpa harus memiliki pengetahuan teknis yang mendalam tentang pemrograman atau machine learning.
Berikut adalah contoh web-app menggunakan Gradio:

gr.Interface() adalah kelas utama dalam Gradio yang digunakan untuk membuat antarmuka pengguna (UI) interaktif untuk model machine learning atau fungsi Python lainnya. Ini memungkinkan developer dengan cepat membuat aplikasi web yang dapat menerima input dari pengguna dan memberikan output atau hasil dari model atau fungsi yang diintegrasikan.
Berikut adalah konsep utama dan parameter gr.Interface:
import gradio as gr
gr.Interface(input=, output=, fn=, live=)
📥Inputs ("input"): Menentukan jenis input yang akan diterima oleh antarmuka. Contoh: "text", "image", "video", "json", "audio" atau kombinasi "text", "image".
📤Outputs ("output"): Menentukan jenis output yang akan dihasilkan oleh antarmuka. Contoh: "text", "image", "video", "json", "audio" atau kombinasi "text", "image".
⚒️ Function ("fn"): Fungsi Python yang akan dijalankan ketika ada input dari pengguna. Bisa berupa model machine learning atau fungsi khusus lainnya.
🌐 Share ("share"): Boolean, menentukan apakah dibuatkan public link atau tidak (temporary 72 hours deployment). Contoh: True atau False.
gr.Interface Parameters:¶title: Judul antarmuka yang akan ditampilkan di halaman web. Contoh: "My Gradio App".
description: Deskripsi antarmuka yang akan ditampilkan di halaman web. Contoh: "A simple app for image classification".
sidebar: Menentukan apakah sidebar akan ditampilkan atau tidak. Contoh: True atau False.
live: Menentukan apakah antarmuka akan memberikan pembaruan langsung. Contoh: True atau False.
Berikut adalah contoh sederhana penggunaan gr.Interface:
import gradio as gr
def my_function(name_text):
return f"Hello, {name_text}. Nice to meet you!"
test = gr.Interface(fn=my_function, inputs="text", outputs="text", live=False, title="Hello World App")
test.launch(share=True)
Running on local URL: http://127.0.0.1:7862 Running on public URL: https://665ebb33c4f2cca4d2.gradio.live This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)
📈 Insight: Pada contoh di atas, kita membuat antarmuka yang menerima input teks dan mengembalikan output teks yang merupakan cetakan dari input pengguna.Kita dapat menggabungkan input dan output yang berbeda serta menyesuaikan parameter sesuai kebutuhan aplikasi yang dibangun.
🔻 Mari kita buat aplikasi aplikasi Gradio yang memungkinkan pengguna melakukan pemindaian dan ekstraksi teks secara real-time pada struktur dokumen seperti kwitansi dan invoice dengan memanfaatkan fungsi doc_to_text yang telah kita rancang sebelumnya.
model.eval()
# code here: let's create gradio
demo = gr.Interface(
inputs="image",
outputs="json",
fn=doc_to_text,
title="Real-time Receipt and Invoice Scanning"
)
demo.launch()
Running on local URL: http://127.0.0.1:7864 To create a public link, set `share=True` in `launch()`.
<s_menu><s_nm> Quele No.3</s_nm><s_num> 09 Jan 2024</s_num><s_price> 13:37</s_price><sep/><s_nm> Receipt Number</s_nm><s_num> YQVSOC</s_num><s_price> QVSOC</s_price><sep/><s_nm> Customer</s_nm><s_num> Benjamin Theodor</s_nm><s_num> Order ID</s_nm><s_num> DTU0003</s_num><s_price> DTU0003</s_price><sep/><s_nm> Bill Name</s_nm><s_num> #An. Benjamin Collected By Dhavina Mutia</s_nm><s_num> *Dine In*</s_num><s_price> 13:37</s_price><sep/><s_nm> Anak Vaksinasi</s_nm><s_num> Anak</s_num><s_unitprice> @30.0000</s_unitprice><s_cnt> 1x</s_cnt><s_price> Theodor</s_price><sep/><s_nm> Konsultasi Dokter An</s_nm><s_num> Konsultasi ak B</s_nm><s_num> ISTLI</s_num><s_unitprice> @305.000</s_unitprice><s_cnt> 1x</s_cnt><s_price> 50.000</s_price><sep/><s_nm> JM Dokter Anak Vaksi nasi</s_nm><s_num> ISTLI</s_num><s_unitprice> @65.000</s_unitprice><s_cnt> 1x</s_cnt><s_price> 65.000</s_price><sep/><s_nm> Vaksin</s_nm><s_num> Prevenar Inj</s_nm><s_unitprice> @1.134.000</s_unitprice><s_cnt> 1x</s_cnt><s_price> 1,134.000</s_price><sep/><s_nm> Vaksin</s_nm><s_unitprice> @452.400</s_unitprice><s_cnt> 1x</s_cnt><s_price> 452.400</s_price><sep/><s_nm> Vaksin</s_nm><s_num> Rotategn</s_nm><s_unitprice> @544.050</s_unitprice><s_cnt> 1x</s_cnt><s_price> 544.050</s_price><sep/><s_nm> Needle 25</s_nm><s_unitprice> @3.500</s_unitprice><s_cnt> 1x</s_cnt><s_price> 3.500</s_price><sep/><s_nm> Tongue Spatula Depre</s_nm><s_num> ssor</s_nm><s_unitprice> @2.500</s_unitprice><s_cnt> 1x</s_cnt><s_price> 2.500</s_price><sep/><s_nm> Plester Anak</s_nm><s_unitprice> @ 1.000</s_unitprice><s_cnt> 2x</s_cnt><s_price> 2.000</s_price><sep/><s_nm> Swab Alkohol</s_nm><s_unitprice> @5.250</s_unitprice><s_cnt> 2x</s_cnt><s_price> 10.500</s_price></s_menu><s_sub_total><s_subtotal_price> Rp 2.568.950</s_subtotal_price><s_discount_price> (Rp 50)</s_discount_price></s_sub_total><s_total><s_total_price> Rp 2.588.900</s_total_price><s_emoneyprice> Rp 2.568.900</s_emoneyprice></s_total>
⚒️ Keunggulan Gradio with Donut:
Efisiensi dan Kepresisian: Mampu mengekstrak teks dari dokumen secara cepat dan akurat, menghemat waktu tanpa perlu proses manual.
Peningkatan Produktivitas: Mempermudah pengelolaan informasi dari kwitansi dan faktur, meningkatkan produktivitas dalam pencatatan keuangan dan pelacakan pengeluaran sehari-hari.
Aksesibilitas dan Kemudahan Penggunaan: Gradio memberikan antarmuka yang intuitif, memungkinkan pengguna mengunggah gambar dokumen dengan cepat tanpa memerlukan keahlian teknis.
Dengan demikian, aplikasi ini tidak hanya berpotensi memberikan solusi teknis, tetapi juga meningkatkan efisiensi dan kenyamanan dalam mengelola informasi dari dokumen struktural seperti kwitansi dan faktur.
✨ Tutorial:
Tutorial: https://www.philschmid.de/fine-tuning-donut
dss_donut dengan python=3.10conda activate dss_donutpip install -r requirements.txtimport re
import os
import torch
import gradio as gr
from PIL import Image
from transformers import DonutProcessor, VisionEncoderDecoderModel
processor = DonutProcessor.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2")
model = VisionEncoderDecoderModel.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2")
.parquet Data?¶Data CORD-V2 sebenarnya cukup besar, yaitu: https://huggingface.co/datasets/naver-clova-ix/cord-v2
Parquet is a columnar storage file format designed for efficiency and high performance. It is commonly used in big data environments and large-scale data analytics.
import pandas as pd
data_parquet = pd.read_parquet('data_input/test-00000-of-00001-9c204eb3f4e11791.parquet')
data_parquet.head()
🔻 Lalu, bagaimana cara menyimpan data parquet gambar tersebut ke dalam file gambar dan text?
import json
for index, row in df.iterrows():
bytes = row['image']['bytes']
labels = json.loads(row['ground_truth'])['gt_parse']
with open(f'data_input/new/{index}.txt', 'w') as label_f:
label_f.write(str(labels))
with open(f'data_input/new/{index}.png', 'wb') as f:
f.write(bytes)